package ca.kooki.codesnippet;

import java.text.ParseException;
import java.util.ArrayList;

import junit.framework.Assert;

import org.junit.After;
import org.junit.Test;

import ca.kooki.codesnippet.core.Action;
import ca.kooki.codesnippet.core.ActionResponse;
import ca.kooki.codesnippet.core.SearchInput;
import ca.kooki.codesnippet.core.SystemDate;
import ca.kooki.codesnippet.core.User;

public abstract class CodeSnippetRepositoryTest 
{

  public abstract CodeSnippetRepository newRepository();
  
  @After
  public void tearDown()
  {
    SystemDate.reset();
  }
  
  @Test
  public void Add()
  {  
    CodeSnippetRepository repo = newRepository();
    
    CodeSnippet snippet = new CodeSnippet("t","k","c");
    ActionResponse response = repo.addCodeSnippet(snippet);
    
    Assert.assertEquals(true,response.isValid());

    CodeSnippet[] allSnippets = repo.getAllSnippets();
    Assert.assertEquals(1, allSnippets.length);
    Assert.assertEquals(snippet, allSnippets[0]);
    
  }
  
  @Test
  public void AddMultiple()
  {
    CodeSnippetRepository repo = newRepository();
    
    CodeSnippet snippet1 = new CodeSnippet("t","k","c1");
    ActionResponse response = repo.addCodeSnippet(snippet1);
    Assert.assertEquals(true,response.isValid());
    
    CodeSnippet snippet2 = new CodeSnippet("t","k","c2");
    response = repo.addCodeSnippet(snippet2);
    Assert.assertEquals(true,response.isValid());  
  
    CodeSnippet[] allSnippets = repo.getAllSnippets();
    Assert.assertEquals(2, allSnippets.length);
    Assert.assertEquals(snippet1, allSnippets[0]);
    Assert.assertEquals(snippet2, allSnippets[1]);
  }
  
  @Test
  public void Empty()
  {
    CodeSnippetRepository repo = newRepository();

    CodeSnippet[] allSnippets = repo.getAllSnippets();
    Assert.assertEquals(0, allSnippets.length);
  }
  
  @Test
  public void SetIdOnSnippet()
  {
    CodeSnippetRepository repo = newRepository();
    
    CodeSnippet snippet = new CodeSnippet("t","k","c");
    ActionResponse response = repo.addCodeSnippet(snippet);
    Assert.assertEquals(true,response.isValid());
    
    Assert.assertEquals(true,snippet.isOfficial());
  }

  @Test
  public void GetSnippetsRetrievesIds()
  {
    CodeSnippetRepository repo = newRepository();
    
    CodeSnippet snippet = new CodeSnippet("t","k","c");
    ActionResponse response = repo.addCodeSnippet(snippet);
    Assert.assertEquals(true,response.isValid());
    
    CodeSnippet[] allSnippets = repo.getAllSnippets();
    CodeSnippet saved = allSnippets[0];
    
    Assert.assertEquals(snippet.getId(),saved.getId());
  }

  @Test
  public void NeverAddTheSameCodeSnippetId()
  {
    CodeSnippetRepository repo = newRepository();
    
    ArrayList<String> uniqueIds = new ArrayList<String>();
    
    for (int i=0; i<10; i++)
    {
      CodeSnippet snippet = new CodeSnippet("t" + i,"k" + i,"c" + i);
      ActionResponse response = repo.addCodeSnippet(snippet);
      Assert.assertEquals(true,response.isValid());
      
      Assert.assertEquals(false, uniqueIds.contains(snippet.getId()));
      uniqueIds.add(snippet.getId());
    }
  }

  @Test
  public void NoUsers()
  {
    CodeSnippetRepository repo = newRepository();
    User[] allUsers = repo.getAllUsers();
    
    Assert.assertEquals(0, allUsers.length);
  }
  
  @Test
  public void AddUser()
  {
    CodeSnippetRepository repo = newRepository();
    User user = new User("aforward");
    
    ActionResponse response = repo.addUser(user);
    Assert.assertEquals(true, response.isValid());
    
    User[] allUsers = repo.getAllUsers();
    Assert.assertEquals(1, allUsers.length);
    Assert.assertEquals("aforward", allUsers[0].getName());
    Assert.assertEquals(user.getId(), allUsers[0].getId());
  }
  
  @Test
  public void AddUserSetsId()
  {
    CodeSnippetRepository repo = newRepository();
    User user = new User("aforward");
    
    repo.addUser(user);
    Assert.assertEquals(true, user.isOfficial());
  }

  @Test
  public void CannotAddSameUserTwice()
  {
    CodeSnippetRepository repo = newRepository();
    User user = new User("aforward");
    
    repo.addUser(user);
    
    User same = new User("aforward");
    ActionResponse response = repo.addUser(same);
    
    Assert.assertEquals(false, response.isValid());
    Assert.assertEquals(true, response.isError("existing_user_name"));
  }

  @Test
  public void CannotAddEmptyUserName()
  {
    CodeSnippetRepository repo = newRepository();
    User user = new User("");
    ActionResponse response = repo.addUser(user);
    
    Assert.assertEquals(false, response.isValid());
    Assert.assertEquals(true, response.isError("empty_user_name"));
  }

  @Test
  public void CannotAddEmptySnippet()
  {
    CodeSnippetRepository repo = newRepository();
    CodeSnippet snippet = new CodeSnippet("","","");
    ActionResponse response = repo.addCodeSnippet(snippet);
    
    Assert.assertEquals(false, response.isValid());    
    Assert.assertEquals(true, response.isError("no_code_snippet"));
  }
  
  @Test
  public void NeverAddTheSameUserId()
  {
    CodeSnippetRepository repo = newRepository();
    
    ArrayList<String> uniqueIds = new ArrayList<String>();
    
    for (int i=0; i<10; i++)
    {
      User user = new User("aforward" + i);
      ActionResponse response = repo.addUser(user);
      Assert.assertEquals(true,response.isValid());
      
      Assert.assertEquals(false, uniqueIds.contains(user.getId()));
      uniqueIds.add(user.getId());
    }
  }
  
  @Test
  public void AddExistingCodeSnippet()
  {
    CodeSnippetRepository repo = newRepository();
    
    CodeSnippet snippet = new CodeSnippet("t","k"," 123");
    repo.addCodeSnippet(snippet);
    
    CodeSnippet same = new CodeSnippet("t1","k1","123 ");
    repo.addCodeSnippet(same);
    
    Assert.assertEquals(snippet.getId(), same.getId());
    Assert.assertEquals(1,repo.getAllSnippets().length);
    
  }

  
  @Test
  public void addTagNoUser()
  {
    CodeSnippetRepository repo = newRepository();
    
    CodeSnippet snippet = new CodeSnippet("t","k","c");
    Tag tag = new Tag("t2","k2",snippet,"");
    
    ActionResponse response = repo.addTag(tag);
    
    Assert.assertEquals(false, response.isValid());
    Assert.assertEquals(true, response.isError("no_user_for_tag"));
    
  }

  @Test
  public void IfUnknownUserThenAddHerWhenAddingATag()
  {
    CodeSnippetRepository repo = newRepository();
    
    CodeSnippet snippet = new CodeSnippet("t","k","c");
    Tag tag = new Tag("t2","k2",snippet,"aforward");
    
    ActionResponse response = repo.addTag(tag);
    
    Assert.assertEquals(true, response.isValid());
    Assert.assertEquals(1, repo.getAllUsers().length);
  }

  @Test
  public void GetAllTagsRetrieveLastUpdated() throws ParseException
  {
    CodeSnippetRepository repo = newRepository();
    
    SystemDate.setNowTo("Nov 4, 2003 8:14 PM");
    CodeSnippet snippet = new CodeSnippet("t","k","c");
    Tag tag = new Tag("t2","k2",snippet,"aforward");
    repo.addTag(tag);
    
    SystemDate.setNowTo("Nov 5, 2003 8:14 PM");

    Tag sameTag = repo.getAllTags(null)[0];
    Assert.assertEquals(SystemDate.parse("Nov 4, 2003 8:14 PM"), sameTag.getUpdated());
    
  }

  @Test
  public void GetAllTagsIgnoresDuplicates()
  {
    CodeSnippetRepository repo = newRepository();
    
    CodeSnippet snippet = new CodeSnippet("t","k","c");
    Tag tag = new Tag("t2","k2",snippet,"aforward");
    repo.addTag(tag);

    Tag sameTag = new Tag("t3","k3",snippet,"aforward");
    repo.addTag(sameTag);
    
    Tag[] allTags = repo.getAllTags("");
    Assert.assertEquals(1, allTags.length);
    Assert.assertEquals(tag, allTags[0]);
  }
  
  
  @Test
  public void AddThenRecoverATag()
  {
    CodeSnippetRepository repo = newRepository();
    
    CodeSnippet snippet = new CodeSnippet("t","k","c");
    Tag tag = new Tag("t2","k2",snippet,"aforward");
    ActionResponse response = repo.addTag(tag);
    
    Assert.assertEquals(true, response.isValid());
    Assert.assertEquals(true, tag.isOfficial());
    
    Tag[] allTags = repo.getAllTags("");
    Assert.assertEquals(1, allTags.length);
    Assert.assertEquals(tag, allTags[0]);
    
  }

  @Test
  public void AddTagWithInvalidSnippet()
  {
    CodeSnippetRepository repo = newRepository();
    
    CodeSnippet snippet = new CodeSnippet("t","k","");
    Tag tag = new Tag("t2","k2",snippet,"aforward");
    ActionResponse response = repo.addTag(tag);
    
    Assert.assertEquals(false, response.isValid());
    Assert.assertEquals(true, response.isError("no_code_snippet"));
  }

  
  @Test
  public void getAllTagsNone()
  {
    CodeSnippetRepository repo = newRepository();
    
    Tag[] allTags = repo.getAllTags("");
    Assert.assertEquals(0, allTags.length);
  }

  @Test
  public void getAllTagsSearchByUserFirst()
  {
    CodeSnippetRepository repo = newRepository();
    
    CodeSnippet snippet = new CodeSnippet("t","k","c");
    Tag tag = new Tag("t2","k2",snippet,"aforward");
    repo.addTag(tag);

    
    Tag[] allTags = repo.getAllTags("/aforward");
    Assert.assertEquals(1, allTags.length);

    allTags = repo.getAllTags("/james");
    Assert.assertEquals(0, allTags.length);
  
  }

  @Test
  public void getAllTagsSearchByKeywordsNext()
  {
    CodeSnippetRepository repo = newRepository();
    
    CodeSnippet snippet = new CodeSnippet("t","k","c");
    Tag tag = new Tag("t2","k k2",snippet,"aforward");
    repo.addTag(tag);

    CodeSnippet snippet2 = new CodeSnippet("t2","k2","c2");
    Tag tag2 = new Tag("t3","k k3",snippet2,"james");
    repo.addTag(tag2);

    
    Tag[] allTags = repo.getAllTags("k2");
    Assert.assertEquals(1, allTags.length);

    allTags = repo.getAllTags("k");
    Assert.assertEquals(2, allTags.length);
  
  }

  
  @Test
  public void isUser()
  {
    CodeSnippetRepository repo = newRepository();
    
    User user = new User("aforward");
    repo.addUser(user);
    
    Assert.assertEquals(true,repo.isUser("aforward"));
    Assert.assertEquals(false,repo.isUser("notaforward"));
  }
  
  @Test
  public void getUserByUsername()
  {
    CodeSnippetRepository repo = newRepository();
    
    User user = null;
    
    user = repo.getUserByUsername(null);
    Assert.assertEquals("", user.getName());
    Assert.assertEquals(false, user.isOfficial());
    
    user = repo.getUserByUsername("");
    Assert.assertEquals("", user.getName());
    Assert.assertEquals(false, user.isOfficial());
    
    user = repo.getUserByUsername("unknown");
    Assert.assertEquals("unknown", user.getName());
    Assert.assertEquals(false, user.isOfficial());
    
    user = new User("aforward");
    repo.addUser(user);
    
    User sameUser = repo.getUserByUsername("aforward");
    Assert.assertEquals("aforward", sameUser.getName());
    Assert.assertEquals(true, sameUser.isOfficial());
    
  }
  
  @Test
  public void getAllSearchesNone()
  {
    CodeSnippetRepository repo = newRepository();
    
    SearchInput[] allSearches = repo.getAllSearches(null);
    Assert.assertEquals(0, allSearches.length);
  }

  @Test
  public void getAllSearchesIgnoreNull()
  {
    CodeSnippetRepository repo = newRepository();
    repo.getAllTags(null);
    Assert.assertEquals(0, repo.getAllSearches(null).length);
  }
  
  @Test
  public void isValid()
  {
    CodeSnippetRepository repo = newRepository();
    ActionResponse response = repo.isValid();
    
    Assert.assertEquals(true,response.isValid());
    Assert.assertEquals("Your repository is configured properly", response.getMessage());
  }
  
  @Test
  public void getAllSearchesTrackUser()
  {
    
    
    CodeSnippetRepository repo = newRepository();
    User user = new User("james");
    repo.addUser(user);
    
    repo.setUser("james");
    repo.getAllTags("/aforward");
    SearchInput[] allSearches = repo.getAllSearches(null);
    
    Assert.assertEquals(user, allSearches[0].from());
  }
  
  @Test
  public void getAllSearchesAll()
  {
    CodeSnippetRepository repo = newRepository();
    
    repo.getAllTags("/aforward");
    repo.getAllTags("/aforward/k1");
    repo.getAllTags("/james/k1");
    
    SearchInput[] allSearches = null;
                
    allSearches = repo.getAllSearches(null);
    Assert.assertEquals(3, allSearches.length);
    
    allSearches = repo.getAllSearches("");
    Assert.assertEquals(3, allSearches.length);
  }
  
  @Test 
  public void getAllActionsNone()
  {
    CodeSnippetRepository repo = newRepository();
    
    Action[] allActions = repo.getAllActions();
    Assert.assertEquals(0, allActions.length);
  }
  
  @Test
  public void getAllActionsAddNewUser()
  {
    CodeSnippetRepository repo = newRepository();
    
    User user = new User("aforward");
    repo.addUser(user);
    Action action = repo.getAllActions()[0];
    Action expected = new Action("new user","created aforward");
    Assert.assertEquals(expected, action);
  }

  @Test
  public void getAllActionsAddCodeSnippet()
  {
    CodeSnippetRepository repo = newRepository();

    CodeSnippet snippet = new CodeSnippet("t","k"," 123");
    repo.addCodeSnippet(snippet);

    Action action = repo.getAllActions()[0];
    Action expected = new Action("new code snippet","added t");
    Assert.assertEquals(expected, action);
    
  }

  @Test
  public void getAllActionsUpdateTag()
  {
    
    CodeSnippetRepository repo = newRepository();
    
    CodeSnippet snippet = new CodeSnippet("t","k","c");
    Tag tag = new Tag("t2","k k2",snippet,"aforward");
    repo.addTag(tag);
    repo.addTag(tag);

    Action[] allActions = repo.getAllActions();
    Action action = allActions[allActions.length-1];
    Action expected = new Action("update tag","[1] t2");
    Assert.assertEquals(expected, action);
  }

  
  @Test
  public void getAllActionsAddTag()
  {
    
    CodeSnippetRepository repo = newRepository();
    
    CodeSnippet snippet = new CodeSnippet("t","k","c");
    Tag tag = new Tag("t2","k k2",snippet,"aforward");
    repo.addTag(tag);

    Action[] allActions = repo.getAllActions();
    Action action = allActions[allActions.length-1];
    Action expected = new Action("new tag","t2");
    Assert.assertEquals(expected, action);
  }

  @Test
  public void addTagDoNotTrackSearch()
  {
    CodeSnippetRepository repo = newRepository();

    CodeSnippet snippet = new CodeSnippet("t","k","c");
    Tag tag = new Tag("t2","k2",snippet,"aforward");
    
    repo.addTag(tag);
    Assert.assertEquals(3,repo.getAllActions().length);
  }

  
  @Test
  public void doNotTrackNullSearches()
  {
    CodeSnippetRepository repo = newRepository();
    repo.getAllTags(null);
    Assert.assertEquals(0,repo.getAllActions().length);
  }

  
  @Test
  public void getAllActionsPerformSearch()
  {
    CodeSnippetRepository repo = newRepository();
    repo.getAllTags("/aforward");

    Action action = repo.getAllActions()[0];
    Action expected = new Action("new search","found [0]: /aforward");
    Assert.assertEquals(expected, action);
  }
  
  @Test
  public void getAndSetUser()
  {
    CodeSnippetRepository repo = newRepository();
    Assert.assertEquals(User.ANNONYMOUS, repo.getUser());
    
    User user = new User("aforward");
    repo.addUser(user);
    
    repo.setUser(user);
    Assert.assertEquals(user, repo.getUser());
    
    repo.setUser("aforward");
    Assert.assertEquals(user, repo.getUser());
  }
  
  @Test
  public void setUserToNull()
  {
    CodeSnippetRepository repo = newRepository();
    repo.setUser((User)null);
    Assert.assertEquals(User.ANNONYMOUS, repo.getUser());

    repo.setUser("");
    Assert.assertEquals(User.ANNONYMOUS, repo.getUser());

    repo.setUser((String)null);
    Assert.assertEquals(User.ANNONYMOUS, repo.getUser());

  }
  
  @Test
  public void getStatisticsBasedOnAllUserTags()
  {
    CodeSnippetRepository repo = newRepository();
    
    CodeSnippet snippet = new CodeSnippet("t","k","c");
    Tag tag = new Tag("t2","k2",snippet,"aforward");

    CodeSnippet snippet2 = new CodeSnippet("t","k","c2");
    Tag tag2 = new Tag("t3","k2 k1",snippet2,"james");
    
    repo.addTag(tag);
    repo.addTag(tag2);

    StatisticSummary summary = repo.getStatistics();
    Assert.assertEquals(2, summary.numberOfCodeSnippets());
    
  }
  
  @Test
  public void addExistingTagUpdatesIt()
  {
    CodeSnippetRepository repo = newRepository();
    
    CodeSnippet snippet = new CodeSnippet("t","k","c");
    Tag tag = new Tag("t2","k2",snippet,"aforward");
    
    
    repo.addTag(tag);
    
    Tag sameTag = new Tag("t3","k3",snippet,"aforward");
    sameTag.setId(tag.getId());
    
    repo.addTag(tag);
    
    Tag[] allTags = repo.getAllTags("/aforward");
    
    Assert.assertEquals(1, allTags.length);
    Assert.assertEquals(sameTag, allTags[0]);
    
    
  }
  
}
